// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import global from '../config/global';

const TRACE_ID_PREFIX = 'trace_';
const DELAY_CONNECTOR = ' - ';
// 阶段性耗时计算将会使用该数组顺序，故该值需保持时序。
const ACTION_TAGS = ['receive', 'parse', 'decode', 'play'];
const ACTION_STATUS_TAGS = {
  start: 'start',
  end: 'end',
  message: 'message'
};
const TRACE_ID_MAX_NUM = 10000;

class DelayAnalysis {
  constructor(tags = [], size = 10000) {
    this.tags = tags;
    this.size = size;
    this.recordCache = [];
    this.traceIds = {};
  }

  allocTraceId() {
    return TRACE_ID_PREFIX + Date.now();
  }

  // 缓存traceId。存取间按顺序处理数据包且不便于带入traceId 参数的情况下，可根据先进先出原则存取traceId
  cacheTraceId(type, traceId) {
    if (!this.traceIds[type]) {
      this.traceIds[type] = [];
    } else if (this.traceIds[type].length >= TRACE_ID_MAX_NUM) {
      this.traceIds[type].shift();
    }

    this.traceIds[type].push(traceId || '');
  }

  shiftTraceId(type) {
    if (!this.traceIds[type]) {
      return 0;
    }

    return this.traceIds[type].shift();
  }

  // 收集打点数据。tags至少包含ACTION_TAGS 和ACTION_STATUS_TAGS 的成员
  record(tags, message, timestamp) {
    if (!global.__IS_DEBUG__) {
      return;
    }

    const now = timestamp || (performance && performance.now());
    this.cache(tags, message, now);
  }

  cache(tags, message, timestamp) {
    if (this.recordCache.length >= this.size) {
      this.recordCache.shift();
    }

    this.recordCache.push({
      timestamp,
      tags,
      message
    });
  }

  /**
   * 原始打点数据按照traceId归类处理
   * @returns {object} traces: {traceId1: {id: traceId1, tag1: timestamp1, tag2: timestamp2...}, traceId2...};
   * sortTags: tag列表，打印traces信息是可以作为表头
   **/
  trace() {
    let traces = {};
    let sortTags = [];
    this.recordCache.forEach((item) => {
      let tags = item.tags || [];
      let traceId = tags.find && tags.find((tag) => tag.startsWith(TRACE_ID_PREFIX));
      let isMsg = tags.includes && tags.includes('message');
      let name = tags.filter && tags.filter((tag) => !tag.startsWith('trace_')).join('|');
      if (traceId) {
        !traces[traceId] && (traces[traceId] = { id: traceId });
        traces[traceId][name] = isMsg ? item.message : item.timestamp;
        if (!sortTags.includes(name)) {
          sortTags.push(name);
        }
      }
    });

    return {
      traces,
      sortTags
    };
  }

  /**
   * 原始数据基础上计算出traceId各阶段耗时
   * @returns {object} traces: 增加了tag间耗时信息的traces；sortTags: tag列表，打印traces信息是可以作为表头
   **/
  analysis() {
    let data = this.trace();
    let traces = data.traces;
    let sortTags = ['id'];
    let hasSortTags = false;
    Object.keys(traces).forEach((id) => {
      const trace = traces[id];
      const tags = Object.keys(trace);
      let preTrace = null;
      // 按照ACTION_TAGS的顺序计算阶段性耗时
      ACTION_TAGS.forEach((action) => {
        const startTag = tags.find(
          (tag) => tag.indexOf(action) > -1 && tag.indexOf(ACTION_STATUS_TAGS.start) > -1
        );
        const endTag = tags.find(
          (tag) => tag.indexOf(action) > -1 && tag.indexOf(ACTION_STATUS_TAGS.end) > -1
        );
        const msgTags = tags.filter(
          (tag) => tag.indexOf(action) > -1 && tag.indexOf(ACTION_STATUS_TAGS.message) > -1
        );

        if (preTrace && (startTag || endTag)) {
          const firstTag = startTag || endTag;
          const newTag = firstTag + DELAY_CONNECTOR + preTrace.tag;
          !hasSortTags && sortTags.push(newTag);
          trace[newTag] = (trace[firstTag] - preTrace.timestamp).toFixed(3);
        }

        !hasSortTags && startTag && sortTags.push(startTag);

        if (startTag && endTag) {
          !hasSortTags && sortTags.push(endTag + DELAY_CONNECTOR + startTag);
          trace[endTag + DELAY_CONNECTOR + startTag] = (trace[endTag] - trace[startTag]).toFixed(3);
        }

        const lastTag = endTag || startTag;
        lastTag &&
          (preTrace = {
            tag: lastTag,
            timestamp: trace[lastTag]
          });

        if (!hasSortTags) {
          endTag && sortTags.push(endTag);
          msgTags.forEach((item) => sortTags.push(item));
        }
      });

      preTrace = null;
      // 认为每个traceId所有的tag名称是一样
      hasSortTags = true;
    });

    return {
      traces,
      sortTags
    };
  }

  exportFile(needAnalysis) {
    let data = null;
    if (needAnalysis) {
      data = this.analysis();
    } else {
      data = this.trace();
    }

    const traces = data.traces;
    const sortTags = data.sortTags;
    let csvContent = sortTags.join(',') + '\n';
    Object.keys(traces).forEach((id) => {
      let trace = traces[id];
      sortTags.forEach((tag) => {
        csvContent += trace[tag] + ',';
      });
      csvContent += '\n';
    });

    let cvsData = new Blob([csvContent], { type: 'application/octet-stream' });
    let anchor = document.createElement('a');
    anchor.href = window.URL.createObjectURL(cvsData);
    anchor.download = `delay-analysis-${Date.now()}.csv`;
    document.body.appendChild(anchor);
    anchor.click();
    document.body.removeChild(anchor);
    window.URL.revokeObjectURL(cvsData);
  }
}

let delayAnalysis = new DelayAnalysis(ACTION_TAGS, 100000);
export default delayAnalysis;
